""""顶象官网滑块 https://www.dingxiang-inc.com/business/captcha"""
# -*- coding: utf-8 -*-
# @Date    : 2024/2/22
# @Author  : zouy
# @Email: 874112501@qq.com
import os
import random
import time
import math
import string

import requests
import execjs
from loguru import logger
import cv2.Error

from utils import DIR_PATH, img2array, array_restore, download_img, get_slide_distance


class DingXiangSlide:
    """顶象官网滑块"""

    def __init__(self, ak):
        self.param_ak = ak
        self.domain_constid = 'https://constid.dingxiang-inc.com'
        self.domain_cap = 'https://cap.dingxiang-inc.com'
        self.request_params = {
            'w': "380",  # 宽度(获取的图片信息是200*400，所以我们在计算距离时，应该按比率缩小)
            'h': "165",  # 高度
            'jsv': "5.1.47",  # 版本号
            'uid': '',
        }
        self.ss = self.set_session()
        self.ctx = self.compile_js_code('ac_token.js')

    @staticmethod
    def set_session():
        """设置session"""
        session = requests.Session()
        session.headers = {
            "Accept": "*/*",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Origin": "https://www.dingxiang-inc.com",
            "Pragma": "no-cache",
            "Referer": "https://www.dingxiang-inc.com/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
        }
        return session

    @staticmethod
    def compile_js_code(file):
        """读取js代码"""
        with open(os.path.join(DIR_PATH, file), 'r', encoding='utf-8') as f:
            js_code = f.read()
        return execjs.compile(js_code)

    @staticmethod
    def get_param_aid():
        """生成请求参数中的 aid"""
        t = int(time.time() * 1000)
        random8str = random.randint(10000000, 99999999)
        return f'dx-{t}-{random8str}-3'

    @staticmethod
    def get_random32_str(self):
        """获取32位随机字符"""
        s = string.digits + string.ascii_letters
        return ''.join(random.choices(s, k=32))

    def request_a_captcha(self, const_id, aid):
        """1.获取验证码图片 /api/a"""
        url = self.domain_cap + '/api/a'
        params = {
            "w": self.request_params['w'],
            "h": self.request_params['h'],
            "s": "50",
            "ak": self.param_ak,
            "c": const_id,
            "jsv": self.request_params['jsv'],
            "aid": aid,
            "wp": "0",  # 图片类型。（0表示jpg, 1表示webp。目前只有chrome支持）
            "de": "0",
            "uid": "",
            "lf": "0",
            "tpc": "",
            # "t": param_t,
            "cid": "68626442",  # 固定
            "_r": str(random.random())
        }
        response = self.ss.get(url, params=params)
        resp_json = response.json()
        sid, y = resp_json['sid'], resp_json['y']
        bg_p1, slide_p2 = resp_json['p1'], resp_json['p2']
        logger.info(f'/api/a >> {sid=}  {y=}')
        return sid, y, bg_p1, slide_p2

    def request_v1_verify(self, aid, sid, y, const_id, *, distance):
        """4. 滑块验证 /api/v1
        成功：{'success': True,
            'token': '6574CAF6FAEDBB1E5657DCDF1283D1AFBC206DE5BF66B3242E8C2C36B719B962CD5A976646F11515C81C63A4AB04B4D2F82D9EF6783E190BB08ADD09C4B565A7C74CD04C37C61A427D8301042F0465D4',
             'msg': None, 'tp': None, 'sv': None, 'retry': 0, 'ot': None
            }
        失败：{'success': False,
            'token': None,
            'msg': 'retry', 'tp': None, 'sv': None, 'retry': 1, 'ot': None
        }
        """
        url = self.domain_cap + '/api/v1'
        distance = int(distance * 380 / 400 - 1)
        time.sleep(2)
        ac = self.ctx.call('slide_ac', sid, distance, y)
        logger.info(f'ac长度:{len(ac)}')
        time.sleep(1)
        data = {
            'ac': ac,
            "ak": self.param_ak,
            "c": const_id,  # c请求返回的data
            "uid": self.request_params['uid'],
            "jsv": self.request_params['jsv'],
            "sid": sid,
            "aid": aid,
            "x": distance,  #
            "y": y,  # 获取图片请求返回y
            "w": self.request_params['w'],
            "h": self.request_params['h']
        }
        response = self.ss.post(url, data=data)
        resp_json = response.json()
        return resp_json

    def run(self):
        """入口"""
        const_id = ''
        # 1 /api/a 获取验证码图片
        aid = self.get_param_aid()
        sid, y, bg_p1, slide_p2 = self.request_a_captcha(const_id, aid)

        # 2.1 下载背景图和滑块图
        img_url = 'https://static.dingxiang-inc.com/picture'
        print(img_url + bg_p1)
        print(img_url + slide_p2)
        bg_img_name = 'bg.png'  # 背景图
        front_img_name = 'front.png'  # 滑块图
        bg_restore_img_name = 'bg_restore.png'  # 还原后的背景图
        bg_path = download_img(bg_img_name, url=img_url + bg_p1)
        download_img(front_img_name, url=img_url + slide_p2)

        # 2.2 图片还原
        img_array = img2array(bg_p1)
        logger.info(f'图片还原数组：{img_array}')
        array_restore(img_array, bg_path, bg_restore_img_name)

        # 2.3 识别滑块距离
        try:
            distance = get_slide_distance(front_img_name, bg_name=bg_restore_img_name)
        except Exception as e:
            logger.warning(f'{type(e)} 滑块距离识别失败： {e}')
            return False
        logger.info(f'{distance=}')

        # 3. 滑块验证 /api/v1
        resp_json = self.request_v1_verify(aid, sid, y, const_id, distance=distance)
        if resp_json.get('success'):
            logger.success(f'验证成功：{resp_json}')
            return True
        else:
            logger.warning(f'验证失败：{resp_json}')
            return False


def get_latest_js():
    """获取最新的greenseer.js文件"""
    _t = math.floor(int(time.time() * 1000) / (3600 * 1000))
    url = 'https://cdn.dingxiang-inc.com/ctu-group/ctu-greenseer/greenseer.js?_t={t}'.format(t=_t)
    resp_text = requests.get(url).text
    path = os.path.join(DIR_PATH, 'greenseer.js')
    with open(path, 'wt', encoding='utf-8') as f:
        f.write(resp_text)
    annotation_text = resp_text.split("\n")[0]
    logger.info(f'下载 greenseer.js?_t={_t} 最新文件：\n{annotation_text}')


if __name__ == '__main__':
    get_latest_js()  # 获取最新的greenseer.js文件(该文件每天需更新同步到本地)
    AK = '99de95ad1f23597c23b3558d932ded3c'  # appKey
    dx = DingXiangSlide(ak=AK)
    for i in range(5):
        if dx.run():
            break
        logger.info(f'运行失败，进行重试 {i + 1}\n')
